﻿Imports System
Imports System.ComponentModel.Design
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.ComponentModel.Compiler
    Friend NotInheritable Class ActivityBindValidator
        Inherits Validator
        ' Methods
        Friend Shared Function DoesTargetTypeMatch(ByVal baseType As Type, ByVal memberType As Type, ByVal access As AccessTypes) As Boolean
            If ((access And AccessTypes.ReadWrite) = AccessTypes.ReadWrite) Then
                Return TypeProvider.IsRepresentingTheSameType(memberType, baseType)
            End If
            If ((access And AccessTypes.Read) = AccessTypes.Read) Then
                Return TypeProvider.IsAssignable(baseType, memberType, True)
            End If
            Return (((access And AccessTypes.Write) = AccessTypes.Write) AndAlso TypeProvider.IsAssignable(memberType, baseType, True))
        End Function

        Public Overrides Function Validate(ByVal manager As ValidationManager, ByVal obj As Object) As ValidationErrorCollection
            Dim errors As ValidationErrorCollection = MyBase.Validate(manager, obj)
            Dim bind As ActivityBind = [TryCast](obj,ActivityBind)
            If (bind Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_UnexpectedArgumentType", New Object() { GetType(ActivityBind).FullName }), "obj")
            End If
            Dim activity As Activity = [TryCast](manager.Context.Item(GetType(Activity)),Activity)
            If (activity Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
            End If
            Dim validationContext As PropertyValidationContext = [TryCast](manager.Context.Item(GetType(PropertyValidationContext)),PropertyValidationContext)
            If (validationContext Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(BindValidationContext).Name }))
            End If
            Dim item As ValidationError = Nothing
            If String.IsNullOrEmpty(bind.Name) Then
                item = New ValidationError(SR.GetString("Error_IDNotSetForActivitySource"), &H613) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                }
                errors.Add(item)
                Return errors
            End If
            Dim refActivity As Activity = Helpers.ParseActivityForBind(activity, bind.Name)
            If (refActivity Is Nothing) Then
                If bind.Name.StartsWith("/") Then
                    item = New ValidationError(SR.GetString("Error_CannotResolveRelativeActivity", New Object() { bind.Name }), &H128)
                Else
                    item = New ValidationError(SR.GetString("Error_CannotResolveActivity", New Object() { bind.Name }), &H129)
                End If
                item.PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name")
                errors.Add(item)
            End If
            If String.IsNullOrEmpty(bind.Path) Then
                item = New ValidationError(SR.GetString("Error_PathNotSetForActivitySource"), &H12B) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                }
                errors.Add(item)
            End If
            If ((Not refActivity Is Nothing) AndAlso (errors.Count = 0)) Then
                Dim path As String = bind.Path
                Dim str2 As String = String.Empty
                Dim startIndex As Integer = path.IndexOfAny(New Char() { "."c, "/"c, "["c })
                If (startIndex <> -1) Then
                    str2 = path.Substring(startIndex)
                    str2 = If(str2.StartsWith("."), str2.Substring(1), str2)
                    path = path.Substring(0, startIndex)
                End If
                Dim baseType As Type = BindHelpers.GetBaseType(manager, validationContext)
                Dim memberInfo As MemberInfo = Nothing
                Dim srcType As Type = Nothing
                If Not String.IsNullOrEmpty(path) Then
                    srcType = BindValidatorHelper.GetActivityType(manager, refActivity)
                    If (Not srcType Is Nothing) Then
                        memberInfo = MemberBind.GetMemberInfo(srcType, path)
                        If ((memberInfo Is Nothing) AndAlso str2.StartsWith("[", StringComparison.Ordinal)) Then
                            Dim str3 As String = bind.Path.Substring(startIndex)
                            Dim index As Integer = str3.IndexOf("]"c)
                            If (index <> -1) Then
                                Dim str4 As String = str3.Substring(0, (index + 1))
                                str2 = If(((index + 1) < str3.Length), str3.Substring((index + 1)), String.Empty)
                                str2 = If(str2.StartsWith("."), str2.Substring(1), str2)
                                str3 = str4
                            End If
                            path = (path & str3)
                            memberInfo = MemberBind.GetMemberInfo(srcType, path)
                        End If
                    End If
                End If
                Dim validator As Validator = Nothing
                Dim obj2 As Object = Nothing
                If (Not memberInfo Is Nothing) Then
                    Dim str5 As String = If(Not String.IsNullOrEmpty(refActivity.QualifiedName), refActivity.QualifiedName, bind.Name)
                    If TypeOf memberInfo Is FieldInfo Then
                        obj2 = New FieldBind((str5 & "." & path), str2)
                        validator = New FieldBindValidator
                    ElseIf TypeOf memberInfo Is MethodInfo Then
                        If GetType(Delegate).IsAssignableFrom(baseType) Then
                            obj2 = New MethodBind((str5 & "." & path))
                            validator = New MethodBindValidator
                        Else
                            item = New ValidationError(SR.GetString("Error_InvalidMemberType", New Object() { path, MyBase.GetFullPropertyName(manager) }), &H629) { _
                                .PropertyName = MyBase.GetFullPropertyName(manager) _
                            }
                            errors.Add(item)
                        End If
                    ElseIf TypeOf memberInfo Is PropertyInfo Then
                        If (refActivity Is activity) Then
                            obj2 = New PropertyBind((str5 & "." & path), str2)
                            validator = New PropertyBindValidator
                        Else
                            obj2 = bind
                            validator = Me
                        End If
                    ElseIf TypeOf memberInfo Is EventInfo Then
                        obj2 = bind
                        validator = Me
                    End If
                ElseIf (((memberInfo Is Nothing) AndAlso (Not baseType Is Nothing)) AndAlso GetType(Delegate).IsAssignableFrom(baseType)) Then
                    obj2 = bind
                    validator = Me
                End If
                If ((Not validator Is Nothing) AndAlso (Not obj2 Is Nothing)) Then
                    If ((validator Is Me) AndAlso TypeOf obj2 Is ActivityBind) Then
                        errors.AddRange(Me.ValidateActivityBind(manager, obj2))
                        Return errors
                    End If
                    errors.AddRange(validator.Validate(manager, obj2))
                    Return errors
                End If
                If (item Is Nothing) Then
                    item = New ValidationError(SR.GetString("Error_PathCouldNotBeResolvedToMember", New Object() { bind.Path, If(Not String.IsNullOrEmpty(refActivity.QualifiedName), refActivity.QualifiedName, refActivity.GetType.Name) }), &H60D) { _
                        .PropertyName = MyBase.GetFullPropertyName(manager) _
                    }
                    errors.Add(item)
                End If
            End If
            Return errors
        End Function

        Private Function ValidateActivity(ByVal manager As ValidationManager, ByVal bind As ActivityBind, ByVal validationContext As BindValidationContext) As ValidationErrorCollection
            Dim item As ValidationError = Nothing
            Dim errors As New ValidationErrorCollection
            Dim activity As Activity = [TryCast](manager.Context.Item(GetType(Activity)),Activity)
            If (activity Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
            End If
            Dim request As Activity = Helpers.ParseActivityForBind(activity, bind.Name)
            If (request Is Nothing) Then
                item = If(bind.Name.StartsWith("/", StringComparison.Ordinal), New ValidationError(SR.GetString("Error_CannotResolveRelativeActivity", New Object() { bind.Name }), &H128), New ValidationError(SR.GetString("Error_CannotResolveActivity", New Object() { bind.Name }), &H129))
                item.PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name")
            ElseIf ((bind.Path Is Nothing) OrElse (bind.Path.Length = 0)) Then
                item = New ValidationError(SR.GetString("Error_PathNotSetForActivitySource"), &H12B) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                }
            Else
                If (Not bind.Name.StartsWith("/", StringComparison.Ordinal) AndAlso Not ValidationHelpers.IsActivitySourceInOrder(request, activity)) Then
                    item = New ValidationError(SR.GetString("Error_BindActivityReference", New Object() { request.QualifiedName, activity.QualifiedName }), &H12A, True) { _
                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                    }
                End If
                Dim service As IDesignerHost = [TryCast](manager.GetService(GetType(IDesignerHost)),IDesignerHost)
                Dim loader As WorkflowDesignerLoader = [TryCast](manager.GetService(GetType(WorkflowDesignerLoader)),WorkflowDesignerLoader)
                If ((Not service Is Nothing) AndAlso (Not loader Is Nothing)) Then
                    Dim srcType As Type = Nothing
                    If (service.RootComponent Is request) Then
                        Dim provider As ITypeProvider = [TryCast](manager.GetService(GetType(ITypeProvider)),ITypeProvider)
                        If (provider Is Nothing) Then
                            Throw New InvalidOperationException(SR.GetString("General_MissingService", New Object() { GetType(ITypeProvider).FullName }))
                        End If
                        srcType = provider.GetType(service.RootComponentClassName)
                    Else
                        request.GetType
                    End If
                    If (Not srcType Is Nothing) Then
                        Dim memberInfo As MemberInfo = MemberBind.GetMemberInfo(srcType, bind.Path)
                        If ((memberInfo Is Nothing) OrElse (TypeOf memberInfo Is PropertyInfo AndAlso Not [TryCast](memberInfo,PropertyInfo).CanRead)) Then
                            item = New ValidationError(SR.GetString("Error_InvalidMemberPath", New Object() { request.QualifiedName, bind.Path }), 300) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        Else
                            Dim memberType As Type = Nothing
                            If TypeOf memberInfo Is FieldInfo Then
                                memberType = DirectCast(memberInfo, FieldInfo).FieldType
                            ElseIf TypeOf memberInfo Is PropertyInfo Then
                                memberType = DirectCast(memberInfo, PropertyInfo).PropertyType
                            ElseIf TypeOf memberInfo Is EventInfo Then
                                memberType = DirectCast(memberInfo, EventInfo).EventHandlerType
                            End If
                            If Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, memberType, validationContext.Access) Then
                                If GetType(WorkflowParameterBinding).IsAssignableFrom(memberInfo.DeclaringType) Then
                                    item = New ValidationError(SR.GetString("Warning_ParameterBinding", New Object() { bind.Path, request.QualifiedName, validationContext.TargetType.FullName }), &H624, True) { _
                                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                    }
                                Else
                                    item = New ValidationError(SR.GetString("Error_TargetTypeMismatch", New Object() { memberInfo.Name, memberType.FullName, validationContext.TargetType.FullName }), &H12D) { _
                                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                    }
                                End If
                            End If
                        End If
                    End If
                Else
                    Dim info2 As MemberInfo = MemberBind.GetMemberInfo(request.GetType, bind.Path)
                    If ((info2 Is Nothing) OrElse (TypeOf info2 Is PropertyInfo AndAlso Not [TryCast](info2,PropertyInfo).CanRead)) Then
                        item = New ValidationError(SR.GetString("Error_InvalidMemberPath", New Object() { request.QualifiedName, bind.Path }), 300) { _
                            .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                        }
                    Else
                        Dim dependencyProperty As DependencyProperty = DependencyProperty.FromName(info2.Name, info2.DeclaringType)
                        Dim obj2 As Object = BindHelpers.ResolveActivityPath(request, bind.Path)
                        If (obj2 Is Nothing) Then
                            Dim fromType As Type = Nothing
                            If TypeOf info2 Is FieldInfo Then
                                fromType = DirectCast(info2, FieldInfo).FieldType
                            ElseIf TypeOf info2 Is PropertyInfo Then
                                fromType = DirectCast(info2, PropertyInfo).PropertyType
                            ElseIf TypeOf info2 Is EventInfo Then
                                fromType = DirectCast(info2, EventInfo).EventHandlerType
                            End If
                            If (Not TypeProvider.IsAssignable(GetType(ActivityBind), fromType) AndAlso Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, fromType, validationContext.Access)) Then
                                If GetType(WorkflowParameterBinding).IsAssignableFrom(info2.DeclaringType) Then
                                    item = New ValidationError(SR.GetString("Warning_ParameterBinding", New Object() { bind.Path, request.QualifiedName, validationContext.TargetType.FullName }), &H624, True) { _
                                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                    }
                                Else
                                    item = New ValidationError(SR.GetString("Error_TargetTypeMismatch", New Object() { info2.Name, fromType.FullName, validationContext.TargetType.FullName }), &H12D) { _
                                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                    }
                                End If
                            End If
                        ElseIf (TypeOf obj2 Is ActivityBind AndAlso (Not request.Parent Is Nothing)) Then
                            Dim bind2 As ActivityBind = [TryCast](obj2,ActivityBind)
                            Dim flag As Boolean = False
                            Dim context As BindRecursionContext = [TryCast](manager.Context.Item(GetType(BindRecursionContext)),BindRecursionContext)
                            If (context Is Nothing) Then
                                context = New BindRecursionContext
                                manager.Context.Push(context)
                                flag = True
                            End If
                            If context.Contains(activity, bind) Then
                                item = New ValidationError(SR.GetString("Bind_ActivityDataSourceRecursionDetected"), &H12F) { _
                                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                }
                            Else
                                context.Add(activity, bind)
                                Dim propertyValidationContext As PropertyValidationContext = Nothing
                                If (Not dependencyProperty Is Nothing) Then
                                    propertyValidationContext = New PropertyValidationContext(request, dependencyProperty)
                                Else
                                    propertyValidationContext = New PropertyValidationContext(request, [TryCast](info2,PropertyInfo), info2.Name)
                                End If
                                errors.AddRange(ValidationHelpers.ValidateProperty(manager, request, bind2, propertyValidationContext, validationContext))
                            End If
                            If flag Then
                                manager.Context.Pop
                            End If
                        ElseIf ((Not validationContext.TargetType Is Nothing) AndAlso Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, obj2.GetType, validationContext.Access)) Then
                            item = New ValidationError(SR.GetString("Error_TargetTypeMismatch", New Object() { info2.Name, obj2.GetType.FullName, validationContext.TargetType.FullName }), &H12D) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        End If
                    End If
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

        Private Function ValidateActivityBind(ByVal manager As ValidationManager, ByVal obj As Object) As ValidationErrorCollection
            Dim errors As ValidationErrorCollection = MyBase.Validate(manager, obj)
            Dim bind As ActivityBind = [TryCast](obj,ActivityBind)
            Dim validationContext As PropertyValidationContext = [TryCast](manager.Context.Item(GetType(PropertyValidationContext)),PropertyValidationContext)
            If (validationContext Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(BindValidationContext).Name }))
            End If
            If Not TypeOf manager.Context.Item(GetType(Activity)) Is Activity Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
            End If
            Dim item As ValidationError = Nothing
            Dim context2 As BindValidationContext = [TryCast](manager.Context.Item(GetType(BindValidationContext)),BindValidationContext)
            If (context2 Is Nothing) Then
                Dim baseType As Type = BindHelpers.GetBaseType(manager, validationContext)
                If (Not baseType Is Nothing) Then
                    Dim accessType As AccessTypes = BindHelpers.GetAccessType(manager, validationContext)
                    context2 = New BindValidationContext(baseType, accessType)
                End If
            End If
            If (Not context2 Is Nothing) Then
                Dim targetType As Type = context2.TargetType
                If (item Is Nothing) Then
                    errors.AddRange(Me.ValidateActivity(manager, bind, New BindValidationContext(targetType, context2.Access)))
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

    End Class
End Namespace

